#!/usr/bin/python3
'''
这个文件负责目录树的实现，它并不基于树，而是基于链表，搜索并不高效
包含两个类：FD_File FileDirectory
'''
import json
import string

class FD_File():
    def __init__(self, ftype='FILE', level=0, name='', parent_name=''): 
        '''
        只能把parent用parent_name，不然就又都是套娃了，关键在于dump那边就变麻烦了
        '''
        if FD_File.check_file_name(name) == 1 and FD_File.check_file_name(parent_name) == 1:
            self.type = ftype # 文件类型
            self.level = level # 文件级别
            self.name = name # 文件名
            self.parent_name = parent_name # 文件所在文件夹
        else:
            print('illegal letters in name')
            raise ValueError

    @staticmethod
    def check_file_name(name):
        '''
        检查文件名格式
        '''
        legal_letters = string.digits+string.ascii_letters+'_.'
        for i in range(0, len(name)):
            if name[i] not in legal_letters:
                return 0
        return 1

class FileDirectory():
    '''
    在这里，我考虑根据level将文件File组成list形式。
    查找删除都是在list内删除，尽管这样是暴力形式，当每层的文件数过多时，文件查找不高效
    暂时还没有实现树形结构访问
    '''
    MAX_FILE_LEVEL = 8 # 最大的文件层数为8）
    MIN_FILE_LEVEL = 0 # 最小为0（根目录level为0）
    
    def __init__(self):
        # 对于level0，设置为根目录，是一个虚拟的目录，只是方便后续操作，后续FD_addfile无法添加该虚拟文件夹
        # 且这里不是列表，因为有且只有一个根目录
        self.filechain = [[FD_File('FOLDER', 0, '', '')]]
        for i in range(0, FileDirectory.MAX_FILE_LEVEL):
            self.filechain.append([])

    @classmethod
    def FD_checkfile(cls, file):
        '''
        判断file是否是满足FileDirectory要求的
        如果是返回1，否则查看返回码和FD_errors
        '''
        if isinstance(file, FD_File):
            # 判断是否超出最大层或者最小层
            if file.level <= FileDirectory.MAX_FILE_LEVEL and file.level >= FileDirectory.MIN_FILE_LEVEL:
                return 1
            else:
                return -2  
        else:
            return -1

    def FD_in_directory(self, file):
        '''
        判断file是否在目录中，是返回对应的位置，否则返回-3
        改：20210927 这里还要考虑parent_name是否一致，加的是否文件名和类型可以确定重复，但删除的时候又需要注意parent_name
        ''' 
        for i in range(0, len(self.filechain[file.level])):
            if self.filechain[file.level][i].name == file.name and self.filechain[file.level][i].type == file.type:
                return i # 这个0让人有点难受 
        return -3
    
    def FD_addfile(self, file):
        '''
        添加这里也涉及到文件名重复（文件类型也一样）的处理方式，一概不允许
        替换则要求将重复的文件先删除，再上传
        调用本函数需要判断返回值
        '''
        if FileDirectory.FD_checkfile(file) < 0:
            return flag
        elif self.FD_in_directory(file) >= 0:
            return -4 # >=0 表示在文件目录中了
        else:
            self.filechain[file.level].append(file) # 不重复直接添加进来
            return 1 # 正确添加则为1
             

    def FD_deletefile(self, file):
        '''
        如果成功删除则返回1，这个函数可以删除所有子文件和子文件夹
        调用本函数需要判断返回值
        '''
        flag = FileDirectory.FD_checkfile(file)
        if flag < 0:
            return flag
        flag = self.FD_in_directory(file)
        if flag < 0:
            return flag
        del self.filechain[file.level][flag] # 删除自己
        # 如果是最后一层或者类型为FILE
        if file.level == FileDirectory.MAX_FILE_LEVEL or file.type == 'FILE':
            return 1
        else: # 如果不是最后一层且还不是文件，就需要继续递归调用下一层
            for i in range(len(self.filechain[file.level+1])-1, -1, -1): # 改成从后往前删除
                if self.filechain[file.level+1][i].parent_name == file.name:
                    self.FD_deletefile(self.filechain[file.level+1][i])
            return 1

    def FD_clearfile(self):
        for i in range(1, len(self.filechain)):
            self.filechain[i] = []

    def FD_dump(self):
        '''
        将目录结构以json的形式dump出去，便于传输
        '''
        temp_list = []
        for i in range(1, len(self.filechain)):
            for j in range(0, len(self.filechain[i])):
                temp_list.append(self.filechain[i][j].__dict__)
        dump_content = json.dumps(temp_list, ensure_ascii=False)
        return dump_content

    def FD_dump_to_file(self, path):
        dump_content = self.FD_dump()
        with open(path, mode='w', encoding='utf-8') as f:
            f.write(dump_content)

    def FD_load(self, dump_content):
        self.FD_clearfile() # 先清空列表，再load
        temp_list = json.loads(dump_content)
        for i in range(0, len(temp_list)):
            temp = FD_File(temp_list[i]['type'], temp_list[i]['level'], temp_list[i]['name'], temp_list[i]['parent_name'])
            flag = self.FD_addfile(temp)
            if flag != 1:
                print(flag, ' in FD_load')
                raise ValueError

    def FD_load_from_file(self, path):
        with open(path, mode='r', encoding='utf-8') as f:
            dump_content = f.read()
        self.FD_load(dump_content)

    def FD_child_files(self, file):
        '''
        获取文件的下一级子文件
        返回的temp_list就是filechain的变量，地址一致，非拷贝
        '''
        if FileDirectory.FD_checkfile(file) < 0:
            print(FileDirectory.FD_checkfile(file), ' in FD_child_files\n')
            raise TypeError
        elif self.FD_in_directory(file) < 0:
            print(self.FD_in_directory(file), ' in FD_child_files\n')
            raise TypeError
        elif file.type == 'FILE':
            print('is FILE, without child_files.\n')
            raise TypeError
        else:
            temp_list = []
            for i in range(0, len(self.filechain[file.level+1])):
                if self.filechain[file.level+1][i].parent_name == file.name:
                    temp_list.append(self.filechain[file.level+1][i])
            return temp_list

    def FD_parent_file(self, file):
        if FileDirectory.FD_checkfile(file) < 0:
            print(FileDirectory.FD_checkfile(file), ' in FD_child_files\n')
            raise TypeError
        elif self.FD_in_directory(file) < 0:
            print(self.FD_in_directory(file), ' in FD_child_files\n')
            raise TypeError
        elif file.level == 0:
            print(-5, ' in FD_child_files\n')
            raise TypeError
        else:
            for i in range(0, len(self.filechain[file.level-1])):
                if self.filechain[file.level-1][i].name == file.parent_name and self.filechain[file.level-1][i].type == 'FOLDER':
                    return self.filechain[file.level-1][i]

    def FD_gen_path(self, file):
        '''
        根据file生成它的路径，需要注意下根文件夹
        '''
        level = file.level
        path = file.name
        for i in range(1, level):
            file = self.FD_parent_file(file)
            path = file.name + '/' + path
        return '/' + path

    def FD_gen_path2(self, file):
        '''
        根据file生成它的路径，需要注意下根文件夹，这个是为了统一格式形成的 '/' 和'/1/'
        '''
        level = file.level
        path = file.name
        path = ''
        for i in range(0, level):
            path = file.name + '/' + path
            file = self.FD_parent_file(file)
        path = file.name + '/' + path
        return path

    @staticmethod
    def FD_trans_filename(filepath):
        '''
        根据文件路径得到文件名，'/1/2/data.txt' -> 'data.txt' 或 'D:/data/3.txt' -> '3.txt'
        '''
        file = filepath.split('/')
        return file[-1]


    def FD_test_gen(self):
        '''
        用来构造文件目录，作为测试各个函数的准确性
        '''
        f1 = FD_File('FOLDER', 1, '1', '')
        f2 = FD_File('FOLDER', 2, '2', '1')
        f3 = FD_File('FOLDER', 2, '3', '1')
        f4 = FD_File('FILE', 2, '4', '1')
        f5 = FD_File('FOLDER', 3, '5', '2')
        f6 = FD_File('FILE', 3, '6', '2')
        f7 = FD_File('FILE', 3, '7', '3')

        f = [f1,f2,f3,f4,f5,f6,f7]

        for i in range(0,len(f)):
            self.FD_addfile(f[i])
    
    def FD_test_show(self):
        temp_list = []
        for i in range(1, len(self.filechain)):
            for j in range(0, len(self.filechain[i])):
                temp_list.append(self.filechain[i][j].name)
        print(temp_list)

    @classmethod
    def FD_errors(error_type=-1):
        '''
        根据数字返回对应的错误类型
        '''
        if error_type == -1:
            return '不是FD_File类型'
        elif error_type == -2:
            return '超出最大层或者最小层'
        elif error_type == -3:
            return '没有找到该文件'
        elif error_type == -4:
            return '添加文件时，文件名重复'
        elif error_ypt == -5:
            return '根目录无法后退'
        else:
            return '未知类型'

# r = './task/project/data/3.txt'
# file = FileDirectory.FD_gen_filename(r)
# print(file)
#data = FileDirectory()
# data.FD_gen_path2()
# f1 = FD_File('FOLDER', 0, '', '')
# print(data.FD_gen_path(f1))
#data.FD_dump_to_file()
# data.FD_test_show()
# data2 = FileDirectory()
# f1 = FD_File('FOLDER', 0, '', '')
# data.FD_child_files(f1)
# f2 = FD_File('FOLDER', 2, '4', '1')
# f3 = FD_File('FOLDER', 2, '3', '1')
# f4 = FD_File('FILE', 2, '4', '1')
# f5 = FD_File('FOLDER', 3, '5', '2')
# f7 = FD_File('FILE', 3, '7', '3')
# t = data.FD_child_files(f7)
# print(t)
# print(data.filechain[3])
# for i in range(0, len(t)):
#     print(t[i].name)

# a = data.FD_deletefile(f1)
# print(a)
# data.FD_test_show()
# data2.FD_load_from_file()
# data2.FD_test_show()
# print(data2.filechain)






